/*
 * @Author: wangming
 * @Date: 2020-09-02 11:29:04
 * @LastEditors: wangming
 * @LastEditTime: 2022-07-12 13:48:31
 * @Description: file content
 */
import axios from 'axios'
import Qs from 'qs'
import { Notification, Message } from 'element-ui'
import stores from 'store2'
import { appUnique, Accesstoken } from "@/utils/auth";
import {
  ACCESS_TOKEN,
  TOKEN,
  MOS_ENABLED,
  APP_PREVIEW,
  APP_UNIQUE
} from '@/utils/const'
import store from '@/store'
import { cacheAdapter } from './cacheAdapter.js'
import packageJson from '../../package.json'
const CancelToken = axios.CancelToken
const requests = {}
const requestList = []
const sources = {}
const instance = axios.create({
  adapter: cacheAdapter(axios.defaults.adapter, {
    useCache: false, // 是否全局开启缓存
    maxAge: 3000 // 缓存时间为3s
  }),
  timeout: 20000,
  headers: {
    [APP_UNIQUE]: appUnique ? 'hm_' + appUnique : '',
    "Client-Type": "PC",
  },
  paramsSerializer: function (params) {
    return Qs.stringify(params, { encode: true, arrayFormat: 'brackets' })
  },
})
instance.interceptors.request.use(
  (config) => {
    addRequest(config)
    if (config.vuekey) {
      // 拦截重复的且未完成的请求
      requests[config.vuekey] && requests[config.vuekey]('网络繁忙')
      // 为每个请求设置名字
      config.cancelToken = new CancelToken(function executor(c) {
        requests[config.vuekey] = c
      })
    }
    const mosEnabled = stores.get(MOS_ENABLED)
    if (process.env.VUE_APP_USER === 'APP') {
      config.headers[ACCESS_TOKEN] = Accesstoken.getAccessToken()
      config.headers['Accept-Language'] = stores.get("APP_LANG")
      //有些接口不需要apptoken
      if (config.hideAppToken) {
        delete config.headers[ACCESS_TOKEN]
      }
    } else {
      config.headers['Authorization'] = 'Bearer ' + Accesstoken.getAccessToken()
    }
    if (mosEnabled) {
      config.headers[TOKEN] = Accesstoken.getAccessToken()
    }
    // 预览
    config.headers[APP_PREVIEW] = process.env.VUE_APP_USER === 'APP'
    config.headers['Designer-Version'] = packageJson.version
    return config
  },
  (error) => {
    return Promise.reject(error)
  }
)

instance.interceptors.response.use(
  async (response) => {
    if (response.headers?.refresh_token) {
      Accesstoken.setAccessToken(response.headers?.refresh_token)
    }
    delete requests[response.config.vuekey] // 删除请求成功的属性
    deleteRequest(response.config)
    if (response.data.code === 0) {
      return response.data.data === undefined
        ? response.data
        : response.data.data
    } else if (response.config.responseType === 'blob' && response.data?.type !== 'application/json') {
      return response
    } else if (typeof response.data === 'string' && response.data.startsWith('<!DOCTYPE html>')) {
      return response?.data
    } else {
      let errorData = response?.data
      if (response.config.responseType === 'blob') {
        errorData = await new Promise((resolve, reject) => {
          const reader = new FileReader()
          reader.readAsText(errorData)
          reader.onload = e => {
            resolve(JSON.parse(e.target.result))
          }
        })
      }
      if (!response.config.nomsg) {
        Message({
          showClose: true,
          message: errorData.message || '网络异常',
          type: 'warning'
        })
      }
      return Promise.reject(errorData)
    }
  },
  (error) => {
    // 删除请求成功的属性
    delete requests[error?.response?.config.vuekey]
    if (error?.response?.config) {
      deleteRequest(error?.response?.config)
    }
    if (axios.isCancel(error)) {
      // 被axios 拦截的场景
      return new Promise(() => { })
    } else if (error?.response?.status === 403) {
      if (process.env.VUE_APP_ROOT_URL === './') {
        cancelAllRequest()
        // 禁止访问资源，即未授权
        Object.values(requests).forEach((callback) => callback(new Error('访问异常')))
        Notification({
          type: 'warning',
          title: '访问异常',
          message: '当前应用未授权，请先授权'
        })
        if (!error?.response?.config.isLogin) {
          store.dispatch('logout')
        }
      }
    } else if (error?.response?.status === 401) {
      cancelAllRequest()
      // 网关不通过，即用户登录信息校验失败，需要用户重新登录
      Object.values(requests).forEach((callback) => callback(new Error('登录异常')))
      Notification({
        type: 'warning',
        title: '登录异常',
        message: '登录信息校验失败，请重新登录'
      })
      store.dispatch('logout')
    } else if (error?.response?.status === 423) {
      cancelAllRequest()
      Object.values(requests).forEach((callback) => callback(new Error('异地登录')))
      Notification({
        type: 'warning',
        title: '异地登录',
        message: '当前账号已在其他设备登录'
      })
      store.dispatch('logout')
    } else if (error?.response?.status === 451) {
      cancelAllRequest()
      Object.values(requests).forEach((callback) => callback(new Error('应用锁定')))
      Notification({
        type: 'warning',
        title: '应用锁定',
        message: '当前应用已锁定'
      })
      store.dispatch('logout')
    } else if (error?.response?.status === 700) {
      cancelAllRequest()
      // 模型自段删除  校验规则检查
    } else {
      cancelAllRequest()
      const message =
        error?.response?.data?.message || error.message || '网络异常'
      Notification({
        type: 'error',
        title: '网络异常',
        message
      })
    }
    return Promise.reject(error)
  },

)
/**
 * 将一个请求转换成对应的字符串，用于比较多个请求是否相同，这里下定义：
 * 当两个请求的method、完整url、全部参数均相同时，则认为这两个请求为“相同请求”
 * @param {*} config
 */
const genUniqueRequest = config => {
  let argStr = ''
  if (!!config && config.hasOwnProperty('params')) {
    argStr = JSON.stringify(config.params)
  } else if (!!config && config.hasOwnProperty('data')) {
    if (config.data instanceof FormData) {
      // 如果是formData则需要这样处理
      argStr = JSON.stringify(Object.fromEntries(config.data))
    } else {
      argStr = JSON.stringify(config.data)
    }
  }
  return `${config.method}_${config.baseURL}${config.url}_${argStr}`
}
/**
 * 将请求添加至列表
 * @param {*} config
 */
const addRequest = config => {
  const request = genUniqueRequest(config)
  // 如果已经有cancelToken就不要新加了，也不加进requestList
  if (!config.cancelToken) {
    config.cancelToken = new CancelToken(cancel => {
      sources[request] = cancel
    })
    requestList.push(request)
    // 1.判断请求是否已存在请求列表，避免重复请求，将当前请求添加进请求列表数组；
    // if (requestList.includes(request)) {
    //   sources[request]('取消重复请求：' + JSON.stringify(request))
    // } else {
    //   requestList.push(request)
    //   // 2. 如果有全局loading状态，在这里置为true
    //   // todo
    // }
  } else {
    console.log(config, 'cancelToken已存在')
  }
}

/**
 * 取消所有请求
 */
const cancelAllRequest = function () {
  requestList.splice(0, requestList.length)
  for (const prop in sources) {
    if (sources.hasOwnProperty(prop)) {
      sources[prop]('cancelAllRequest ' + JSON.stringify(prop))
    }
  }
}

/**
 * 从列表中移除请求
 * @param {*} config
 */
const deleteRequest = config => {
  // 1.将当前请求中请求列表中删除
  const request = genUniqueRequest(config)
  requestList.splice(
    requestList.findIndex(item => item === request),
    1
  )
  // 2.当请求列表为空时，如果有全局loading状态，在这里置为false
  if (requestList.length === 0) {
    // todo
  }
}
Window.prototype.$Request = instance
export default instance
